/**
 * Copyright (c) 2019 Coder League
 * All rights reserved.
 *
 * File：BaseDefaultMQProducer.java
 * History:
 *         2019年7月21日: Initially created, CJH.
 */
package club.coderleague.ilsp.common.rocketmq.producer;

import java.util.Collection;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;

import lombok.extern.slf4j.Slf4j;

/**
 * 消息生产者基础类。
 * 
 * @author CJH
 */
@Slf4j
public class BaseDefaultMQProducer {
	
	/**
	 * NameServer服务地址。
	 */
	@Value("${custom.rocketmq.namesrv-addr}")
    private String namesrvAddr;
	
	/**
	 * 生产者。
	 */
	private DefaultMQProducer producer;
	
	/**
	 * 生产者组。
	 */
	private String producerGroup;
	
	/**
	 * 构造方法。
	 * 
	 * @param producerGroup 生产者组。
	 */
	public BaseDefaultMQProducer(String producerGroup) {
		this.producerGroup = producerGroup;
	}
	
	/**
	 * 初始化方法。
	 * 
	 * @author CJH 2019年7月21日
	 */
	@PostConstruct
	private void init() {
		this.producer = new DefaultMQProducer(this.producerGroup);
		this.producer.setNamesrvAddr(this.namesrvAddr);
		preStart(this.producer);
		try {
			this.producer.start();
			log.info("producer group is the {} startup success of '{}'", DefaultMQProducer.class.getTypeName(), this.producerGroup);
		} catch (MQClientException e) {
			log.error("producer group is the {} startup failure of '{}'", DefaultMQProducer.class.getTypeName(), this.producerGroup);
			e.printStackTrace();
		}
	}
	
	/**
	 * 停止生产者
	 * 
	 * @author CJH 2019年7月22日
	 */
	@PreDestroy
	private void destroy() {
		this.producer.shutdown();
		log.info("producer group is the {} shutdown success of '{}'", DefaultMQProducer.class.getTypeName(), this.producerGroup);
	}
	
	/**
	 * 生产者启动之前执行。
	 * 
	 * @author CJH 2019年7月21日
	 * @param producer 生产者实例。
	 */
	protected void preStart(DefaultMQProducer producer) {}
	
	/**
	 * 以同步模式发送消息。此方法仅在发送过程完全完成时返回。
	 * 
	 * @author CJH 2019年7月21日
	 * @param msg 发送的消息。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg) {
		try {
			return this.producer.send(msg);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Message)}相同，另外还指定了发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param timeout 发送超时。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg, long timeout) {
		try {
			return this.producer.send(msg, timeout);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 以异步模式发送消息。</p>
	 * 
	 * 此方法立即返回。发送完成后，将执行sendCallback。</p>
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param sendCallback 发送完成时执行的回调。
	 */
	public void send(Message msg, SendCallback sendCallback) {
		try {
			this.producer.send(msg, sendCallback);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #send(Message, SendCallback)}相同，另外还指定了发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param sendCallback 发送完成时执行的回调。
	 * @param timeout 发送超时。
	 */
	public void send(Message msg, SendCallback sendCallback, long timeout) {
		try {
			this.producer.send(msg, sendCallback, timeout);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 此方法不会在代理返回前等待确认。显然，它具有最大的吞吐量，但也有可能丢失消息。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 */
	public void sendOneway(Message msg) {
		try {
			this.producer.sendOneway(msg);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #send(Message)}相同，另外还指定了目标消息队列。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param mq 目标消息队列。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg, MessageQueue mq) {
		try {
			return this.producer.send(msg, mq);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Message)}相同，另外还指定了目标消息队列和发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param mq 目标消息队列。
	 * @param timeout 发送超时。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg, MessageQueue mq, long timeout) {
		try {
			return this.producer.send(msg, mq, timeout);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Message, SendCallback)}相同，另外还指定了目标消息队列。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param mq 目标消息队列。
	 * @param sendCallback 发送完成时执行的回调。
	 */
	public void send(Message msg, MessageQueue mq, SendCallback sendCallback) {
		try {
			this.producer.send(msg, mq, sendCallback);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #send(Message, SendCallback)}相同，另外还指定了目标消息队列和发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param mq 目标消息队列。
	 * @param sendCallback 发送完成时执行的回调。
	 * @param timeout 发送超时。
	 */
	public void send(Message msg, MessageQueue mq, SendCallback sendCallback, long timeout) {
		try {
			this.producer.send(msg, mq, sendCallback, timeout);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #sendOneway(Message)}相同，另外还指定了目标消息队列。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param mq 目标消息队列。
	 */
	public void sendOneway(Message msg, MessageQueue mq) {
		try {
			this.producer.sendOneway(msg, mq);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #send(Message)}相同，另外还指定了消息队列选择器。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param selector 消息队列选择器，通过它我们可以获得目标消息队列来传递消息。
	 * @param arg 与消息队列选择器一起使用的参数。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg, MessageQueueSelector selector, Object arg) {
		try {
			return this.producer.send(msg, selector, arg);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Message)}相同，另外还指定了消息队列选择器和发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param selector 消息队列选择器，通过它我们可以获得目标消息队列来传递消息。
	 * @param arg 与消息队列选择器一起使用的参数。
	 * @param timeout 发送超时。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout) {
		try {
			return this.producer.send(msg, selector, arg);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Message, SendCallback)}相同，另外还指定了消息队列选择器。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param selector 消息队列选择器，通过它我们可以获得目标消息队列来传递消息。
	 * @param arg 与消息队列选择器一起使用的参数。
	 * @param sendCallback 发送完成时执行的回调。
	 */
	public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback) {
		try {
			this.producer.send(msg, selector, arg, sendCallback);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #send(Message, SendCallback)}相同，另外还指定了消息队列选择器。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param selector 消息队列选择器，通过它我们可以获得目标消息队列来传递消息。
	 * @param arg 与消息队列选择器一起使用的参数。
	 * @param sendCallback 发送完成时执行的回调。
	 * @param timeout 发送超时。
	 */
	public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback, long timeout) {
		try {
			this.producer.send(msg, selector, arg, sendCallback, timeout);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 与{@link #sendOneway(Message)}相同，另外还指定了消息队列选择器。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msg 发送的消息。
	 * @param selector 消息队列选择器，通过它我们可以获得目标消息队列来传递消息。
	 * @param arg 与消息队列选择器一起使用的参数。
	 */
	public void sendOneway(Message msg, MessageQueueSelector selector, Object arg) {
		try {
			this.producer.sendOneway(msg, selector, arg);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 以同步模式发送批量消息。此方法仅在发送过程完全完成时返回。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msgs 发送的批量消息。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Collection<Message> msgs) {
		try {
			return this.producer.send(msgs);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Collection)}相同，另外还指定了发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msgs 发送的批量消息。
	 * @param timeout 发送超时。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Collection<Message> msgs, long timeout) {
		try {
			return this.producer.send(msgs, timeout);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Collection)}相同，另外还指定了目标消息队列。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msgs 发送的批量消息。
	 * @param mq 目标消息队列。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Collection<Message> msgs, MessageQueue mq) {
		try {
			return this.producer.send(msgs, mq);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 与{@link #send(Collection)}相同，另外还指定了目标消息队列和发送超时。
	 * 
	 * @author CJH 2019年7月23日
	 * @param msgs 发送的批量消息。
	 * @param mq 目标消息队列。
	 * @param timeout 发送超时。
	 * @return {@link SendResult}实例用于通知发件人可交付结果的详细信息。
	 */
	public SendResult send(Collection<Message> msgs, MessageQueue mq, long timeout) {
		try {
			return this.producer.send(msgs, mq, timeout);
		} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
}